////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modif   //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#include "visitors/declarationvisitor.hpp"

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

#include <vector>
using std::vector;

#include <string>
using std::string;

#include <boost/shared_ptr.hpp>
using boost::dynamic_pointer_cast;

#include <typeinfo>

#include "ast/nodetypes.hpp"
#include "visitors/expressionnavigator.hpp"
#include "visitors/universalnavigator.hpp"
#include "common.hpp"
#include "messages.hpp"
#include "codelocation.hpp"
#include "path.hpp"
#include "assert.hpp"
#include "accesstype.hpp"
#include "symboltree.hpp"
#include "parser.hpp"

/******************************************************************************
 * DeclarationVisitor1 Class (first pass)                                     *
 ******************************************************************************/

class DeclarationVisitor1 : public UniversalNavigator {
	public:
		
		DeclarationVisitor1(shared_ptr<Messages> messages, shared_ptr<SymbolTree> symbolRoot);
		
		void visit(shared_ptr<ArithmeticOperation>   node);
		void visit(shared_ptr<FunctionCall>          node);
		void visit(shared_ptr<UnaryOperation>        node);
		void visit(shared_ptr<Identifier>            node);
		void visit(shared_ptr<IntegerLiteral>        node);
		void visit(shared_ptr<BooleanLiteral>        node);
		void visit(shared_ptr<Null>                  node);
		void visit(shared_ptr<ArrayType>             node);
		void visit(shared_ptr<IntegerType>           node);
		void visit(shared_ptr<BooleanType>           node);
		void visit(shared_ptr<Assertion>             node);
		void visit(shared_ptr<IfStatement>           node);
		void visit(shared_ptr<WhileStatement>        node);
		void visit(shared_ptr<Assignment>            node);
		void visit(shared_ptr<IdAssignment>          node);
		void visit(shared_ptr<CompoundStatement>     node);
		void visit(shared_ptr<FunctionType>          node);
		void visit(shared_ptr<VoidType>              node);
		void visit(shared_ptr<VariableDeclaration>   node);
		void visit(shared_ptr<FunctionDeclaration>   node);
		void visit(shared_ptr<Declarations>          node);
		void visit(shared_ptr<Subscripting>          node);
		void visit(shared_ptr<ComparisonChain>       node);
		void visit(shared_ptr<TypeError>             node);
		void visit(shared_ptr<AndChain>              node);
		void visit(shared_ptr<OrChain>               node);
		void visit(shared_ptr<ImpliesChain>          node);
		void visit(shared_ptr<ForStatement>          node);
		void visit(shared_ptr<Range>                 node);
		void visit(shared_ptr<RangeType>             node);
		void visit(shared_ptr<Reference>             node);
		void visit(shared_ptr<BooleanPointerLiteral> node);
		void visit(shared_ptr<IntegerPointerLiteral> node);
		
		bool correct_run_function_found() const;
	
	private:
		
		shared_ptr<SymbolTree> _currentScope;
		
		bool _correctRunFunctionFound;
		
		bool _justPassedFunctionDeclaration;
		
		shared_ptr<SymbolTree> current_scope() const;
		void set_current_scope(shared_ptr<SymbolTree> currentScope);
		void leave_current_scope();
		void add_parameter_symbols(shared_ptr<FunctionDeclaration> node);
		bool check_array_size(shared_ptr<Type> type);
		void check_for_redeclaration(shared_ptr<Identifier> identifier);
};


/******************************************************************************
 * DeclarationVisitor2 Class (second pass)                                    *
 ******************************************************************************/

class DeclarationVisitor2 : public ExpressionNavigator {
	public:
		
		void visit(shared_ptr<FunctionCall> node);
		void visit(shared_ptr<Reference>    node);
		void visit(shared_ptr<Assignment>   node);
		void visit(shared_ptr<IdAssignment> node);
};

/******************************************************************************
 * DeclarationVisitor3 Class (third pass)                                     *
 ******************************************************************************/

class DeclarationVisitor3 : public ExpressionNavigator {
	public:
		
		DeclarationVisitor3(shared_ptr<Messages> messages) : ExpressionNavigator(messages), _inAssertion(false) {}
		
		void visit(shared_ptr<FunctionCall> node);
		void visit(shared_ptr<Reference>    node);
		void visit(shared_ptr<Assertion>    node);
	
	private:
		
		bool _inAssertion;
};

/******************************************************************************
 * Function Implementations                                                   *
 ******************************************************************************/

void find_declarations(shared_ptr<Node> node, shared_ptr<Messages> messages, shared_ptr<SymbolTree> symbolRoot) {
	assert(node);
	assert(messages);
	assert(symbolRoot);
	
	shared_ptr<DeclarationVisitor1> visitor1(new DeclarationVisitor1(messages, symbolRoot));
	node->accept(visitor1);
	
	if (!visitor1->correct_run_function_found())
		messages->add("no-run-function");
	
	shared_ptr<DeclarationVisitor2> visitor2(new DeclarationVisitor2);
	node->accept(visitor2);
	
	shared_ptr<DeclarationVisitor3> visitor3(new DeclarationVisitor3(messages));
	node->accept(visitor3);
}

/******************************************************************************/


DeclarationVisitor1::DeclarationVisitor1(shared_ptr<Messages> messages, shared_ptr<SymbolTree> symbolRoot) : UniversalNavigator(messages), _currentScope(symbolRoot), _correctRunFunctionFound(false), _justPassedFunctionDeclaration(false) {
	assert(messages);
	assert(symbolRoot);
}

void DeclarationVisitor1::visit(shared_ptr<ArithmeticOperation> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<FunctionCall> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<UnaryOperation> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<Identifier> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<IntegerLiteral> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<BooleanLiteral> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<Null> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<ArrayType> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<IntegerType> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<BooleanType> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<Assertion> node) {
	assert(node);
	
	node->set_scope(current_scope());
	
	if (node->condition_is_set()) {
		ExpressionNavigator::visit(node);
	} else if (current_scope() == current_scope()->containing_function()) { // No blinder symbols inbetween
		UniversalNavigator::visit(node->variable_declaration());
		
		foreach (VarDec var, node->variable_declaration()->variables()) {
			check_for_redeclaration(var.name);
			
			shared_ptr<Type> type(typeid(*node->variable_declaration()->type()) == typeid(VoidType) || check_array_size(node->variable_declaration()->type()) ? dynamic_pointer_cast<Type>(TypeError::create()) : node->variable_declaration()->type());
			CodeLocation loc(var.name->location().begin(), var.is_initialized() ? var.init->location().end() : var.name->location().begin());
			
			var.name->set_symbol(shared_ptr<NamedSymbol>(new NamedSymbol(var.name->text(), loc, type, AccessType(true, true, true, true), true)));
			node->scope()->insert_child(var.name->symbol());
		}
	} else {
		messages()->add("lv-not-pre", node->variable_declaration()->location());
	}
}

void DeclarationVisitor1::visit(shared_ptr<IfStatement> node) {
	assert(node);
	
	node->set_scope(current_scope());
	
	node->condition()->accept(shared_from_this());
	
	if (typeid(*node->then_statement()) == typeid(CompoundStatement)) {
		node->then_statement()->accept(shared_from_this());
	} else {
		shared_ptr<UnnamedSymbol> newScope(new UnnamedSymbol);
		current_scope()->insert_child(newScope);
		
		set_current_scope(newScope);
		node->then_statement()->accept(shared_from_this());
		leave_current_scope();
	}
	
	if (node->else_statement_is_set()) {
		if (typeid(*node->else_statement()) == typeid(CompoundStatement)) {
			node->else_statement()->accept(shared_from_this());
		} else {
			shared_ptr<UnnamedSymbol> newScope(new UnnamedSymbol);
			current_scope()->insert_child(newScope);
			
			set_current_scope(newScope);
			node->else_statement()->accept(shared_from_this());
			leave_current_scope();
		}
	}
}

void DeclarationVisitor1::visit(shared_ptr<WhileStatement> node) {
	assert(node);
	
	node->set_scope(current_scope());
	
	node->condition()->accept(shared_from_this());
	
	if (typeid(*node->statement()) == typeid(CompoundStatement)) {
		node->statement()->accept(shared_from_this());
	} else {
		shared_ptr<UnnamedSymbol> newScope(new UnnamedSymbol);
		current_scope()->insert_child(newScope);
		
		set_current_scope(newScope);
		node->statement()->accept(shared_from_this());
		leave_current_scope();
	}
}

void DeclarationVisitor1::visit(shared_ptr<Assignment> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<IdAssignment> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<CompoundStatement> node) {
	assert(node);
	
	node->set_scope(current_scope());
	
	if (_justPassedFunctionDeclaration) {
		_justPassedFunctionDeclaration = false;
		foreach (shared_ptr<Statement> statement, node->statements())
			statement->accept(shared_from_this());
	} else if (node->name_is_set()) {
		shared_ptr<NamedSymbol> newScope(new NamedSymbol(node->name()->text(), node->name()->location(), VoidType::create(), AccessType(false, false, false, false)));
		current_scope()->insert_child(newScope);
		
		set_current_scope(newScope);
		foreach (shared_ptr<Statement> statement, node->statements())
			statement->accept(shared_from_this());
		leave_current_scope();
	} else {
		shared_ptr<UnnamedSymbol> newScope(new UnnamedSymbol);
		current_scope()->insert_child(newScope);
		
		set_current_scope(newScope);
		foreach (shared_ptr<Statement> statement, node->statements())
			statement->accept(shared_from_this());
		leave_current_scope();
	}
}

void DeclarationVisitor1::visit(shared_ptr<FunctionType> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<VoidType> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<VariableDeclaration> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
	
	shared_ptr<Type> t = node->type();
	while (typeid(*t) == typeid(ArrayType))
		t = dynamic_pointer_cast<ArrayType>(t)->content_type();
	
	if (typeid(*t) == typeid(VoidType))
		messages()->add("void-vars", node->type()->location());
	
	shared_ptr<Type> type(typeid(*t) == typeid(VoidType) || check_array_size(node->type()) ? dynamic_pointer_cast<Type>(TypeError::create()) : node->type());
	
	foreach (VarDec var, node->variables()) {
		check_for_redeclaration(var.name);
		
		CodeLocation loc(var.name->location().begin(), var.is_initialized() ? var.init->location().end() : var.name->location().begin());
		
		// For now, no way to make a constant variable (ReadOnly)
		var.name->set_symbol(shared_ptr<NamedSymbol>(new NamedSymbol(var.name->text(), loc, type, AccessType(true, true, true, true))));
		node->scope()->insert_child(var.name->symbol());
	}
}

void DeclarationVisitor1::visit(shared_ptr<FunctionDeclaration> node) {
	assert(node);
	
	node->set_scope(current_scope());
	
	node->name()->accept(shared_from_this());
	node->type()->accept(shared_from_this());
	
	// Check if this is the run function
	if (node->name()->text() == "run") {
		shared_ptr<FunctionType> ftype(node->type());
		if (ftype->equals(parse<FunctionType>("(int)function()")))
			_correctRunFunctionFound = true;
	}
	
	check_for_redeclaration(node->name());
	node->name()->set_symbol(shared_ptr<NamedSymbol>(new NamedSymbol(node->name()->text(), node->name()->location(), node->type(), AccessType(true, false, true, false))));
	current_scope()->insert_child(node->name()->symbol());
	
	set_current_scope(node->name()->symbol());
		
		foreach (shared_ptr<Assertion> assertion, node->preconditions())
			assertion->accept(shared_from_this());
		
		shared_ptr<BlinderSymbol> blinder1(new BlinderSymbol);
		current_scope()->insert_child(blinder1);
		
		set_current_scope(blinder1);
			
			foreach (shared_ptr<Assertion> assertion, node->postconditions())
				assertion->accept(shared_from_this());
			
			shared_ptr<Type> t = node->type()->return_type();
			
			if (typeid(*t) == typeid(ArrayType))
				messages()->add("return-array", node->type()->return_type()->location());
			
			if (t->weak_pointer())
				messages()->add("weak-return", node->type()->return_type()->location());
			
			if (typeid(*t) != typeid(VoidType)) {
				shared_ptr<Identifier> resultIdentifier = Identifier::create("result");
				resultIdentifier->set_location(node->type()->return_type()->location());
				
				check_for_redeclaration(resultIdentifier);
				current_scope()->insert_child(shared_ptr<NamedSymbol>(new NamedSymbol(resultIdentifier->text(), resultIdentifier->location(), (t->weak_pointer() || typeid(*t) == typeid(ArrayType) ? dynamic_pointer_cast<Type>(TypeError::create()) : dynamic_pointer_cast<Type>(node->type()->return_type())), AccessType(true, true, true, true))));
			}
			
		leave_current_scope();
		
		// We're adding the parameter symbols AFTER the result symbol, so any `result'
		// parameter (rather than the real result var) will be a redeclaration error
		add_parameter_symbols(node);
		
		set_current_scope(blinder1);
			
			shared_ptr<BlinderSymbol> blinder2(new BlinderSymbol);
			current_scope()->insert_child(blinder2);
			
			set_current_scope(blinder2);
				
				_justPassedFunctionDeclaration = true;
				node->statement()->accept(shared_from_this());
				
			leave_current_scope();
			
		leave_current_scope();
		
	leave_current_scope();
}

void DeclarationVisitor1::visit(shared_ptr<Declarations> node) {
	assert(node);
	
	node->set_scope(current_scope());
	StatementNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<Subscripting> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<ComparisonChain> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<TypeError> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<AndChain> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<OrChain> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<ImpliesChain> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<ForStatement> node) {
	assert(node);
	
	node->set_scope(current_scope());
	
	node->range()->accept(shared_from_this());
	
	if (typeid(*node->statement()) == typeid(CompoundStatement)) {
		// The CompoundStatement code must be handled here, because
		// the iterator has this statement as its scope
		shared_ptr<CompoundStatement> csNode(dynamic_pointer_cast<CompoundStatement>(node->statement()));
		csNode->set_scope(current_scope());
		
		shared_ptr<UnnamedSymbol> newScope(new UnnamedSymbol);
		current_scope()->insert_child(newScope);
		
		set_current_scope(newScope);
			// Add iterator
			node->iterator()->set_symbol(shared_ptr<NamedSymbol>(new NamedSymbol(node->iterator()->text(), node->iterator()->location(), IntegerType::create(), AccessType(false, false, true, false)))); // iterator has no identity anymore
			current_scope()->insert_child(node->iterator()->symbol());
			
			foreach (shared_ptr<Statement> statement, csNode->statements())
				statement->accept(shared_from_this());
		leave_current_scope();
	} else {
		shared_ptr<UnnamedSymbol> newScope(new UnnamedSymbol);
		current_scope()->insert_child(newScope);
		
		set_current_scope(newScope);
			// Add iterator
			node->iterator()->set_symbol(shared_ptr<NamedSymbol>(new NamedSymbol(node->iterator()->text(), node->iterator()->location(), IntegerType::create(), AccessType(false, false, true, false)))); // iterator has no identity anymore
			current_scope()->insert_child(node->iterator()->symbol());
			
			node->statement()->accept(shared_from_this());
		leave_current_scope();
	}
}

void DeclarationVisitor1::visit(shared_ptr<Range> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<RangeType> node) {
	assert(node);
	
	node->set_scope(current_scope());
	UniversalNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<Reference> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<BooleanPointerLiteral> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

void DeclarationVisitor1::visit(shared_ptr<IntegerPointerLiteral> node) {
	assert(node);
	
	node->set_scope(current_scope());
	ExpressionNavigator::visit(node);
}

/********************/

bool DeclarationVisitor1::correct_run_function_found() const {
	return _correctRunFunctionFound;
}

shared_ptr<SymbolTree> DeclarationVisitor1::current_scope() const {
	assert(_currentScope);
	
	return _currentScope;
}

void DeclarationVisitor1::set_current_scope(shared_ptr<SymbolTree> currentScope) {
	assert(currentScope);
	
	_currentScope = currentScope;
}

void DeclarationVisitor1::leave_current_scope() {
	assert(!current_scope()->is_root());
	
	set_current_scope(current_scope()->parent());
}

void DeclarationVisitor1::add_parameter_symbols(shared_ptr<FunctionDeclaration> node) {
	assert(node);
	
	for (unsigned int i = 0; i < node->parameter_name_count(); ++i) {
		node->parameter_names()[i]->accept(shared_from_this());
		
		check_for_redeclaration(node->parameter_names()[i]);
		
		shared_ptr<Type> t = node->type()->parameter_types()[i].type;
		shared_ptr<ParameterPolicy> p = node->type()->parameter_types()[i].policy;
		
		if (typeid(*t) == typeid(ArrayType) && p->identityPassed == false && p->formalAccess.identity.readable)
			messages()->add("by-val-array", node->type()->parameter_types()[i].type->location());
		
		while (typeid(*t) == typeid(ArrayType))
			t = dynamic_pointer_cast<ArrayType>(t)->content_type();
		
		if (typeid(*t) == typeid(VoidType))
			messages()->add("void-params", node->type()->parameter_types()[i].type->location());
		
		
		node->parameter_names()[i]->set_symbol(shared_ptr<NamedSymbol>(new NamedSymbol(node->parameter_names()[i]->text(), node->parameter_names()[i]->location(), (typeid(*t) == typeid(VoidType) || check_array_size(node->type()->parameter_types()[i].type) ? dynamic_pointer_cast<Type>(TypeError::create()) : dynamic_pointer_cast<Type>(node->type()->parameter_types()[i].type)), node->type()->parameter_types()[i].policy->formalAccess)));
		current_scope()->insert_child(node->parameter_names()[i]->symbol());
	}
}

bool DeclarationVisitor1::check_array_size(shared_ptr<Type> type) {
	bool arrayError = false;
	while (typeid(*type) == typeid(ArrayType)) {
		shared_ptr<ArrayType> atype = dynamic_pointer_cast<ArrayType>(type);
		if (atype->size()->value() > 0xFFFFFFFF) {
			messages()->add("array-size-limit", atype->size()->location());
			arrayError = true;
		}
		type = atype->content_type();
	}
	return arrayError;
}

void DeclarationVisitor1::check_for_redeclaration(shared_ptr<Identifier> identifier) {
	if (current_scope()->has_local_symbol(identifier->text())) {
		string newName;
		unsigned int counter = 1;
		do {
			newName = identifier->text() + "(" + to_string(++counter) + ")";
		} while (current_scope()->symbol_is_visible(newName));
		
		messages()->add("redeclaration", identifier->location())
			% identifier->text()
			% ordinal(counter);
		
		// Change the name in the ast
		identifier->set_text(newName);
	}
}

/******************************************************************************/

void DeclarationVisitor2::visit(shared_ptr<FunctionCall> node) {
	assert(node);
	
	ExpressionNavigator::visit(node);
	
	// The language can only use an identifier as a function-call base right now
	assert(typeid(*node->base()) == typeid(Reference));
	
	shared_ptr<Reference> base(dynamic_pointer_cast<Reference>(node->base()));
	
	if (node->scope()->symbol_is_visible(base->path())) {
		shared_ptr<NamedSymbol> symbol = node->scope()->get_visible_symbol(base->path());
		
		if (typeid(*symbol->type()) == typeid(FunctionType)) { // Else, it will become an error in the type visitor
			node->scope()->containing_function()->insert_called_function(symbol);
			
			shared_ptr<FunctionType> ftype = dynamic_pointer_cast<FunctionType>(symbol->type());
			
			// Add by-ref and by-als vars to written-var set of the function
			for (unsigned i = 0; i < node->parameter_count() && i < ftype->parameter_type_count(); ++i) { // For each actual parameter
				if (ftype->parameter_types()[i].policy->identityPassed) { // If it was passed by-ref or by-als
					shared_ptr<Expression> expr = node->parameters()[i];
					
					while (typeid(*expr) == typeid(Subscripting))
						expr = dynamic_pointer_cast<Subscripting>(expr)->base();
					
					if (typeid(*expr) == typeid(Reference)) { // And it is a reference (avoid errors)
						shared_ptr<Reference> ref = dynamic_pointer_cast<Reference>(expr);
						if (node->scope()->symbol_is_visible(ref->path())) {
							shared_ptr<NamedSymbol> refSymbol = node->scope()->get_visible_symbol(ref->path());
							if (node->scope()->containing_function() != refSymbol->parent()->containing_function())
								node->scope()->containing_function()->insert_written_var(refSymbol);
						}
					}
				}
			}
		}
	}
}

void DeclarationVisitor2::visit(shared_ptr<Assignment> node) {
	assert(node);
	
	ExpressionNavigator::visit(node);
	
	shared_ptr<Expression> expr = node->left_side();
	
	while (typeid(*expr) == typeid(Subscripting))
		expr = dynamic_pointer_cast<Subscripting>(expr)->base();
	
	if (typeid(*expr) == typeid(Reference)) {
		shared_ptr<Reference> ref = dynamic_pointer_cast<Reference>(expr);
		if (node->scope()->symbol_is_visible(ref->path())) {
			shared_ptr<NamedSymbol> refSymbol = node->scope()->get_visible_symbol(ref->path());
			if (node->scope()->containing_function() != refSymbol->parent()->containing_function())
				node->scope()->containing_function()->insert_written_var(refSymbol);
		}
	}
}

void DeclarationVisitor2::visit(shared_ptr<IdAssignment> node) {
	assert(node);
	
	ExpressionNavigator::visit(node);
	
	shared_ptr<Expression> expr = node->left_side();
	
	while (typeid(*expr) == typeid(Subscripting))
		expr = dynamic_pointer_cast<Subscripting>(expr)->base();
	
	if (typeid(*expr) == typeid(Reference)) {
		shared_ptr<Reference> ref = dynamic_pointer_cast<Reference>(expr);
		if (node->scope()->symbol_is_visible(ref->path())) {
			shared_ptr<NamedSymbol> refSymbol = node->scope()->get_visible_symbol(ref->path());
			if (node->scope()->containing_function() != refSymbol->parent()->containing_function())
				node->scope()->containing_function()->insert_written_var(refSymbol);
		}
	}
	
	expr = node->right_side();
	
	while (typeid(*expr) == typeid(Subscripting))
		expr = dynamic_pointer_cast<Subscripting>(expr)->base();
	
	if (typeid(*expr) == typeid(Reference)) {
		shared_ptr<Reference> ref = dynamic_pointer_cast<Reference>(expr);
		if (node->scope()->symbol_is_visible(ref->path())) {
			shared_ptr<NamedSymbol> refSymbol = node->scope()->get_visible_symbol(ref->path());
			if (node->scope()->containing_function() != refSymbol->parent()->containing_function())
				node->scope()->containing_function()->insert_written_var(refSymbol);
		}
	}
}

void DeclarationVisitor2::visit(shared_ptr<Reference> node) {
	assert(node);
	
	ExpressionNavigator::visit(node);
	
	if (node->scope()->symbol_is_visible(node->path())) {
		shared_ptr<NamedSymbol> symbol(node->scope()->get_visible_symbol(node->path()));
		
		// Only put the reference in the use-set of the containing function if the reference is not local
		if (node->scope()->containing_function() != symbol->parent()->containing_function())
			node->scope()->containing_function()->insert_used_var(symbol);
	}
}

/******************************************************************************/

void DeclarationVisitor3::visit(shared_ptr<FunctionCall> node) {
	assert(node);
	
	ExpressionNavigator::visit(node);
	
	// The language can only use an identifier as a function-call base right now
	assert(typeid(*node->base()) == typeid(Reference));
	
	shared_ptr<Reference> base(dynamic_pointer_cast<Reference>(node->base()));
	
	if (node->scope()->symbol_is_visible(base->path())) {
		shared_ptr<NamedSymbol> symbol(node->scope()->get_visible_symbol(base->path()));
		
		foreach (shared_ptr<NamedSymbol> usedVar, symbol->used_vars_recursive()) {
			// We only allow forward references for non-writable (id and val) symbols. They must have
			// gotten a value at declaration which cannot be interfered with anymore.
			if (usedVar->access_type().value.writable || usedVar->access_type().identity.writable) {
				bool declBeforeRef = usedVar->declaration_location() < node->location();
				bool isLocal       = node->scope()->containing_function() == usedVar->containing_function();
				if (!declBeforeRef && isLocal) {
					messages()->add("undeclared-at-call", node->location())
						% base->path()
						% usedVar->name();
				}
			}
		}
	}
}

void DeclarationVisitor3::visit(shared_ptr<Reference> node) {
	assert(node);
	
	if (node->scope()->symbol_is_visible(node->path())) {
		node->set_symbol(node->scope()->get_visible_symbol(node->path()));
		
		// At this point, symbol is declared in current scope or a shallower visible scope
		
		// We only allow forward references for non-writable (id and val) symbols. They must have
		// gotten a value and identity at declaration which cannot be interfered with anymore.
		
		bool declBeforeRef = node->symbol()->declaration_location().end() < node->location().begin();
		if (node->symbol()->logical()) {
			bool isInPrecondition = (node->scope() == node->symbol()->containing_function());
			if (!declBeforeRef && isInPrecondition) {
				messages()->add("undeclared", node->location())
					% node->path();
			} else if (!_inAssertion) {
				messages()->add("lv-in-impl", node->location())
					% node->path();
			}
		} else if (node->symbol()->access_type().value.writable || node->symbol()->access_type().identity.writable) {
			bool isLocal       = node->scope()->containing_function() == node->symbol()->containing_function();
			if (!declBeforeRef && isLocal) {
				messages()->add("undeclared", node->location())
					% node->path();
			}
		}
	} else {
		messages()->add("undeclared", node->location())
			% node->path();
	}
}

void DeclarationVisitor3::visit(shared_ptr<Assertion> node) {
	_inAssertion = true;
	ExpressionNavigator::visit(node);
	_inAssertion = false;
}
